<?php
/**
 * 错误处理句柄.
 *
 * @author Qiong Wu <papa0924@gmail.com>
 * @copyright ©2003-2103 phpwind.com
 * @license http://www.windframework.com
 *
 * @version $Id$
 */
abstract class WindError extends WindModule
{
    protected $errorDir;
    protected $isClosed;

    /**
     * 构造方法.
     *
     * @param string $errorDir 错误目录地址
     * @param bool   $isClosed 站点是否关闭
     */
    public function __construct($errorDir, $isClosed)
    {
        $this->errorDir = $errorDir;
        $this->isClosed = $isClosed;
    }

    /**
     * 异常处理句柄.
     *
     * @param Exception $exception
     */
    public function exceptionHandle($exception)
    {
        $trace = array();
        $file = $line = '';
        if (Wind::$isDebug) {
            $trace = $exception->getTrace();
            if (@$trace[0]['file'] == '') {
                unset($trace[0]);
                $trace = array_values($trace);
            }
            $file = @$trace[0]['file'];
            $line = @$trace[0]['line'];
        }
        $this->showErrorMessage($exception->getMessage(), $file, $line, $trace,
            $exception->getCode());
    }

    /**
     * 错误处理句柄.
     *
     * @param int    $errno
     * @param string $errstr
     * @param string $errfile
     * @param int    $errline
     */
    public function errorHandle($errno, $errstr, $errfile, $errline)
    {
        $trace = array();
        if (Wind::$isDebug) {
            $trace = debug_backtrace();
            unset($trace[0]['function'], $trace[0]['args']);
        }
        $this->showErrorMessage($this->_friendlyErrorType($errno).': '.$errstr, $errfile,
            $errline, $trace, $errno);
    }

    /**
     * 错误处理.
     *
     * @param string $message
     * @param string $file      异常文件
     * @param int    $line      错误发生的行
     * @param array  $trace
     * @param int    $errorcode 错误代码
     *
     * @throws WindFinalException
     */
    abstract protected function showErrorMessage($message, $file, $line, $trace, $errorcode);

    /**
     * 错误信息处理方法.
     *
     * @param string $file
     * @param string $line
     * @param array  $trace
     */
    protected function crash($file, $line, $trace)
    {
        if ($trace) {
            $msg = '';
            $count = count($trace);
            $padLen = strlen($count);
            foreach ($trace as $key => $call) {
                if (!isset($call['file']) || $call['file'] == '') {
                    $call['file'] = '~Internal Location~';
                    $call['line'] = 'N/A';
                }
                $traceLine = '#'.str_pad(($count - $key), $padLen, '0', STR_PAD_LEFT).'  '.$this->_getCallLine(
                    $call);
                $trace[$key] = $traceLine;
            }
        }
        $fileLines = array();
        if (is_file($file)) {
            $currentLine = $line - 1;
            $fileLines = explode("\n", file_get_contents($file, null, null, 0, 10000000));
            $topLine = $currentLine - 5;
            $fileLines = array_slice($fileLines, $topLine > 0 ? $topLine : 0, 10, true);
            if (($count = count($fileLines)) > 0) {
                $padLen = strlen($count);
                foreach ($fileLines as $line => &$fileLine) {
                    $fileLine = ' '.htmlspecialchars(
                        str_pad($line + 1, $padLen, '0', STR_PAD_LEFT).': '.str_replace("\t",
                            '    ', rtrim($fileLine)), null, 'UTF-8');
                }
            }
        }

        return array($fileLines, $trace);
    }

    /**
     * @param array $call
     *
     * @return string
     */
    private function _getCallLine($call)
    {
        $call_signature = '';
        if (isset($call['file'])) {
            $call_signature .= $call['file'].' ';
        }
        if (isset($call['line'])) {
            $call_signature .= '('.$call['line'].') ';
        }
        if (isset($call['function'])) {
            $call_signature .= $call['function'].'(';
            if (isset($call['args'])) {
                foreach ($call['args'] as $arg) {
                    if (is_string($arg)) {
                        $arg = '"'.(strlen($arg) <= 64 ? $arg : substr($arg, 0, 64).'…').'"';
                    } elseif (is_object($arg)) {
                        $arg = ''.get_class($arg).'';
                    } elseif ($arg === true) {
                        $arg = 'true';
                    } elseif ($arg === false) {
                        $arg = 'false';
                    } elseif ($arg === null) {
                        $arg = 'null';
                    } elseif (is_array($arg)) {
                        $arg = WindString::varToString($arg);
                    } else {
                        $arg = strval($arg);
                    }
                    $call_signature .= $arg.',';
                }
            }
            $call_signature = trim($call_signature, ',').')';
        }

        return $call_signature;
    }

    /**
     * 返回友好的错误类型名.
     *
     * @param int $type
     *
     * @return string unknown
     */
    private function _friendlyErrorType($type)
    {
        switch ($type) {
            case E_ERROR:
                return 'E_ERROR';
            case E_WARNING:
                return 'E_WARNING';
            case E_PARSE:
                return 'E_PARSE';
            case E_NOTICE:
                return 'E_NOTICE';
            case E_CORE_ERROR:
                return 'E_CORE_ERROR';
            case E_CORE_WARNING:
                return 'E_CORE_WARNING';
            case E_CORE_ERROR:
                return 'E_COMPILE_ERROR';
            case E_CORE_WARNING:
                return 'E_COMPILE_WARNING';
            case E_USER_ERROR:
                return 'E_USER_ERROR';
            case E_USER_WARNING:
                return 'E_USER_WARNING';
            case E_USER_NOTICE:
                return 'E_USER_NOTICE';
            case E_STRICT:
                return 'E_STRICT';
            case E_RECOVERABLE_ERROR:
                return 'E_RECOVERABLE_ERROR';
            case E_DEPRECATED:
                return 'E_DEPRECATED';
            case E_USER_DEPRECATED:
                return 'E_USER_DEPRECATED';
        }

        return $type;
    }
}
